package com.jse;

import java.text.DateFormatSymbols;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.chrono.ChronoLocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.TemporalAccessor;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;

public class Times {

	private static SimpleDateFormat COOKIE_FORMAT=new SimpleDateFormat("EEE, dd-MMM-yyyy HH:mm:ss z");
	private static SimpleDateFormat LAST_MODIFIED=new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss z",Locale.ENGLISH);
	private static final Map<String,SimpleDateFormat> DATAFORMAT_MAP = new HashMap<>(16, 0.25F);
	private static final Map<String,DateTimeFormatter> FORMATTER_MAP = new HashMap<>(16, 0.25F);
	static {
		COOKIE_FORMAT.setTimeZone(TimeZone.getTimeZone("GMT"));
	}
	/** cookie时间格式化 */
	public static String cookie(Date d){return COOKIE_FORMAT.format(d);}
	public static String lastModified(Date d){return LAST_MODIFIED.format(d);}
	public static Date lastModifiedDate(String s) throws ParseException{return LAST_MODIFIED.parse(s);}
	public static SimpleDateFormat dateFormat(String fmt) {
		SimpleDateFormat ret= DATAFORMAT_MAP.get(fmt);
		if(ret==null) {
			ret=new SimpleDateFormat(fmt);
			DATAFORMAT_MAP.put(fmt,ret);
		}
		return ret;
	}
	static public DateTimeFormatter formatter(String fmt){
		DateTimeFormatter ret= FORMATTER_MAP.get(fmt);
		if(ret==null) {
			ret=DateTimeFormatter.ofPattern(fmt);
			FORMATTER_MAP.put(fmt,ret);
		}
		return ret;
	}
	public static String format() {
		return format("yyyy-MM-dd HH:mm:ss",LocalDateTime.now());
	}
	public static String format(Date t) {
		return format("yyyy-MM-dd HH:mm:ss",t);
	}
	public static String format(long t) {
		return format("yyyy-MM-dd HH:mm:ss",t);
	}
	public static String format(Date d,String fmt) {
		return format(fmt,d);
	}
	public static String format(String fmt){
		return format(fmt,LocalDateTime.now());
	}
	public static Date parse(String d, String f) {
		try{return dateFormat(f).parse(d);}catch(ParseException e){e.printStackTrace();return null;}
	}
	public static String gmt(Date date) {
        SimpleDateFormat df = dateFormat("E, dd MMM yyyy HH:mm:ss z");
        df.setDateFormatSymbols(DateFormatSymbols.getInstance(Locale.UK));
        df.setTimeZone(new java.util.SimpleTimeZone(0, "GMT"));
        return df.format(date);
    }
	static public String format(String fmt,Object obj){
		if(obj==null)obj=LocalDateTime.now();
		switch (fmt){
			case "G","u","y","D","M/L","d","g","Q/q","Y","w","W","E","e/c","F","a","B","h","K","k","H","m","s","S","A","n","N":
				if(obj instanceof Long l)
					return formatter(fmt).format(LocalDateTime.ofInstant(Instant.ofEpochMilli(l),ZoneId.systemDefault()));
				if(obj instanceof Date d)
					return formatter(fmt).format(LocalDateTime.ofInstant(d.toInstant(),ZoneId.systemDefault()));
				if(obj instanceof TemporalAccessor t)
					return formatter(fmt).format(t);
			case "V","v","z","O","X","x","Z","p","'","''":
			default: {
				if(obj instanceof String s)//格式化仅针对long
					return LocalDateTime.ofInstant(Instant.ofEpochMilli(Long.parseLong(s)),ZoneId.systemDefault()).format(formatter(fmt));
				else if(obj instanceof Integer i)
					return LocalDateTime.ofInstant(Instant.ofEpochSecond(i.longValue()),ZoneId.systemDefault()).format(formatter(fmt));
				else if(obj instanceof Long l)
					return LocalDateTime.ofInstant(Instant.ofEpochMilli(l),ZoneId.systemDefault()).format(formatter(fmt));
				else if(obj instanceof java.sql.Date d)
					return LocalDateTime.ofInstant(new Date(d.getTime()).toInstant(),ZoneId.systemDefault()).format(formatter(fmt));
				else if(obj instanceof Date d)
					return LocalDateTime.ofInstant(d.toInstant(),ZoneId.systemDefault()).format(formatter(fmt));
				else if(obj instanceof Calendar c)
					return LocalDateTime.ofInstant(c.toInstant(),ZoneId.systemDefault()).format(formatter(fmt));
				else if(obj instanceof Instant i)
					return LocalDateTime.ofInstant(i,ZoneId.systemDefault()).format(formatter(fmt));
				else if(obj instanceof TemporalAccessor t)
					return formatter(fmt).format(t);
				else
					return "not support "+obj;
			}
		}
	}
	
	public static LocalTime localTime(Date date) {
		return localDateTime(date).toLocalTime();
	}
	public static LocalDateTime localDateTime(Date date) {
		if (date instanceof java.sql.Date) {// java.sql.Date 不支持 toInstant()
			date = new Date(date.getTime());
		}
		Instant instant = date.toInstant();
		ZoneId zone = ZoneId.systemDefault();
		return LocalDateTime.ofInstant(instant, zone);
	}
	
	public static Date now() {return new Date();}
	
	public static Date date(Object o) {
		if(o instanceof Date d)return d;
		else if(o instanceof Calendar c)return c.getTime();
		else if(o instanceof LocalDate l)return Date.from(l.atStartOfDay(ZoneId.systemDefault()).toInstant());
		else if(o instanceof LocalDateTime l)return Date.from(l.atZone(ZoneId.systemDefault()).toInstant());
		else if(o instanceof Number ms)return new Date(ms.longValue());
		else if(o instanceof String s){
			switch (s.length()){
				case 2:
					Calendar cal=Calendar.getInstance();
					if(s.equals("昨天")){
						cal.add(Calendar.DATE,-1);
						return cal.getTime();
					}else if(s.equals("明天")){
						cal.add(Calendar.DATE,1);
						return cal.getTime();
					}else if(s.equals("后天")) {
						cal.add(Calendar.DATE,2);
						return cal.getTime();
					}else if(s.equals("去年")){
						cal.add(Calendar.YEAR,-1);
						return cal.getTime();
					}else if(s.equals("前年")){
						cal.add(Calendar.YEAR,-2);
						return cal.getTime();
					}else if(s.equals("明年")){
						cal.add(Calendar.YEAR,1);
						return cal.getTime();
					}
					else if(s.equals("后年")){
						cal.add(Calendar.YEAR,2);
						return cal.getTime();
					}
					else return new Date();
				case 8:return Date.from(LocalDate.from(formatter("yyyyMMdd").parse(s)).atStartOfDay(ZoneId.systemDefault()).toInstant());
				case 10:return Date.from(LocalDate.from(formatter("yyyy-MM-dd").parse(s)).atStartOfDay(ZoneId.systemDefault()).toInstant());
				case 14:return Date.from(LocalDateTime.from(formatter("yyyyMMddHHmmss").parse(s)).atZone(ZoneId.systemDefault()).toInstant());
				case 17:return Date.from(LocalDateTime.from(formatter("yyyyMMddHHmmssSSS").parse(s)).atZone(ZoneId.systemDefault()).toInstant());
				case 19:return Date.from(LocalDateTime.from(formatter("yyyy-MM-dd HH:mm:ss").parse(s)).atZone(ZoneId.systemDefault()).toInstant());
				case 23:return Date.from(LocalDateTime.from(formatter("yyyy-MM-dd HH:mm:ss.SSS").parse(s)).atZone(ZoneId.systemDefault()).toInstant());
				default:return null;
			}
		}
		return null;
	}
	public static LocalDateTime toLocalDateTime(Object ldt) {
		if (ldt instanceof LocalDateTime d)return d;
		else if (ldt instanceof LocalDate d)return d.atStartOfDay();
		else if (ldt instanceof LocalTime d)return LocalDateTime.of(LocalDate.now(),d);
		else if (ldt instanceof java.util.Date d)return localDateTime(d);
		else if (ldt instanceof String s) {
			throw new RuntimeException("string case LocalDateTime not impl!");
		}
		return (LocalDateTime)ldt;
	}
	/**
	 * 判断 A 的时间是否在 B 的时间 "之后"
	 */
	public static boolean isAfter(ChronoLocalDateTime<?> self, ChronoLocalDateTime<?> other) {
		return self.isAfter(other);
	}
	/**
	 * 判断 A 的时间是否在 B 的时间 "之前"
	 */
	public static boolean isBefore(ChronoLocalDateTime<?> self, ChronoLocalDateTime<?> other) {
		return self.isBefore(other);
	}
	/**
	 * 判断 A 的时间是否与 B 的时间 "相同"
	 */
	public static boolean isEqual(ChronoLocalDateTime<?> self, ChronoLocalDateTime<?> other) {
		return self.isEqual(other);
	}
	
	private static Instant start;
	
	/** 仅简单测试代码时方便使用 */
	public static void teststart() {start=Instant.now();}
	/** 仅简单测试代码时方便使用 */
	public static void testend() {long m=Duration.between(start, Instant.now()).toMillis();
		System.out.println(String.format("test code (%s)ms",m));
	}
	public static int year(){return LocalDate.now().getYear();}
	public static int year(Date d) {
		return LocalDate.ofInstant(d.toInstant(),ZoneId.systemDefault()).getYear();
	}
	public static int age(Date d1, Date d2) {int a=year(d2)-year(d1);return a<0?-a:a;}
	public static boolean isLeapYear(int year) {
		return year%4==0&&year%100!=0||year%400==0;
	}
	public static long utc() {return System.currentTimeMillis() / 1000L;}
	public static void main(String[] args) {
		System.out.println(Times.format(Times.date(((System.currentTimeMillis() / 1000)-604800)*1000+"")));
	}
}
